// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.designer.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dengues.commons.IDenguesSharedImage;
import org.dengues.commons.connectivity.IConnectivitySettingsConstants;
import org.dengues.commons.utils.FileUtils;
import org.dengues.commons.utils.ImageUtil;
import org.dengues.core.ExceptionOperation;
import org.dengues.core.IDesignerCoreService;
import org.dengues.core.codegen.ICodeGenerator;
import org.dengues.core.components.EGlobalComponentNames;
import org.dengues.core.components.IComponentFilesNaming;
import org.dengues.core.components.IComponentsFactory;
import org.dengues.core.components.IXMLComponent;
import org.dengues.core.metadata.DatabaseContainer;
import org.dengues.core.metadata.MetadataColumn;
import org.dengues.core.metadata.MetadataDatabase;
import org.dengues.core.metadata.MetadataTable;
import org.dengues.core.metadata.MetadataUtils;
import org.dengues.core.module.EClasspathVariables;
import org.dengues.core.module.IModuleScriptSynchronizer;
import org.dengues.core.process.ICompNode;
import org.dengues.core.process.ICompProcess;
import org.dengues.core.resource.DenguesProjectFactory;
import org.dengues.core.resource.WarehouseResourceFactory;
import org.dengues.core.runprocess.IJavaETLProcessor;
import org.dengues.core.warehouse.ENodeCategoryName;
import org.dengues.designer.core.codegen.ProcessCodeGenerator;
import org.dengues.designer.core.codegen.config.JETCodeGeneratorEmittersPoolFactory;
import org.dengues.designer.core.components.ComponentFilesNaming;
import org.dengues.designer.core.components.ComponentsFactory;
import org.dengues.designer.core.data.CompDataProcess;
import org.dengues.designer.core.demos.ImportDemoProjectProvider;
import org.dengues.designer.core.i18n.Messages;
import org.dengues.designer.core.launch.JavaETLProcessor;
import org.dengues.designer.core.shadow.CSVFileOutputNode;
import org.dengues.designer.core.shadow.ShadowCompProcess;
import org.dengues.designer.core.shadow.ShadowFileInputNode;
import org.dengues.designer.core.sqldb.SqldbUtil;
import org.dengues.designer.core.sqldb.StartHsqldbServer;
import org.dengues.designer.core.utils.ModuleScriptSynchronizer;
import org.dengues.model.warehouse.BlockType;
import org.dengues.model.warehouse.ProcessType;
import org.dengues.ui.prefs.connectivity.EConnectivityProperties;
import org.dengues.ui.prefs.connectivity.XMLFileManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.action.Action;
import org.eclipse.swt.widgets.Shell;
import org.osgi.framework.Bundle;

import com.csvreader.CsvReader;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf 2007-11-21 qiang.zhang $
 * 
 */
public class DesignerCoreService implements IDesignerCoreService {

    private static Logger logger = Logger.getLogger(DesignerCoreService.class);

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getComponentFilesNaming()
     */
    public IComponentFilesNaming getComponentFilesNaming() {
        return new ComponentFilesNaming();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getComponentsFactory()
     */
    public IComponentsFactory getComponentsFactory() {
        return new ComponentsFactory();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#initializeTemplates()
     */
    public void initializeTemplates() {
        JETCodeGeneratorEmittersPoolFactory.initialize();
        getJavaProcessor().synDenguesApi();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getJavaProcessor(org.dengues.core.process.ICompProcess)
     */
    public IJavaETLProcessor getJavaProcessor(ICompProcess process, IPath path) {
        return new JavaETLProcessor(process, path);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#launchShadowProcess(org.dengues.core.components.IXMLComponent,
     * java.util.Properties)
     */
    public Process launchShadowProcess(IXMLComponent component, Properties properties) throws Exception {
        ShadowCompProcess shadowCompProcess = new ShadowCompProcess(component, properties);
        final JavaETLProcessor javaETLProcessor = new JavaETLProcessor(shadowCompProcess, new Path("shadow")); //$NON-NLS-1$
        try {
            return javaETLProcessor.run(XMLFileManager.parseString(properties
                    .getProperty(IConnectivitySettingsConstants.PROP_DEFN_JARLIST)));
        } catch (Exception e) {
            ExceptionOperation.operate(e);
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getJavaProcessor()
     */
    public IJavaETLProcessor getJavaProcessor() {
        return new JavaETLProcessor();
    }

    private final StartHsqldbServer startHsqldbServer = new StartHsqldbServer();

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#createStartHsqldbServer()
     */
    public Action createStartHsqldbServer() {
        return startHsqldbServer;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#runSqlScript(org.eclipse.core.resources.IFile)
     */
    public ILaunch runSqlScript(IFile scriptFile, Map properties) {
        try {
            String driver = (String) properties.get(EConnectivityProperties.PROP_ID_DRIVER.getPropId());
            String rootpath = (String) properties.get(EConnectivityProperties.PROP_ID_ROOTPATH.getPropId());
            String database = (String) properties.get(EConnectivityProperties.PROP_ID_DATABASE.getPropId());
            String schema = (String) properties.get(EConnectivityProperties.PROP_ID_SCHEMA.getPropId());
            String url = (String) properties.get(EConnectivityProperties.PROP_ID_URL.getPropId());
            String user = (String) properties.get(EConnectivityProperties.PROP_ID_USER.getPropId());
            String password = (String) properties.get(EConnectivityProperties.PROP_ID_PASSWORD.getPropId());

            return SqldbUtil.runScriptTool(scriptFile, driver, rootpath, database, schema, url, user, password);
        } catch (Exception e) {
            ExceptionOperation.operate(e);
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#checkHsqldbConnection()
     */
    public boolean checkHsqldbConnection() {
        boolean isStart = SqldbUtil.checkConnection();
        StartHsqldbServer.setRunning(isStart);
        startHsqldbServer.setText(Messages.getString("DesignerCoreService.Start.Label")); //$NON-NLS-1$
        if (isStart) {
            startHsqldbServer.setImageDescriptor(ImageUtil.getDescriptor(IDenguesSharedImage.ACTION_STARTDB));
            startHsqldbServer.setText(Messages.getString("DesignerCoreService.Stop.Label")); //$NON-NLS-1$
        }
        return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getCodeGenerator()
     */
    public ICodeGenerator getCodeGenerator() {
        return new ProcessCodeGenerator();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getDatabaseContainerFrommConnection(java.sql.Connection,
     * java.util.Properties)
     */
    public DatabaseContainer getDatabaseContainerFrommConnection(Connection connection, Map baseProperties) {
        DatabaseContainer container = new DatabaseContainer();
        List<MetadataDatabase> bds = new ArrayList<MetadataDatabase>();

        MetadataDatabase database = new MetadataDatabase();
        database.setDatabaseContainer(container);
        String property = (String) baseProperties.get(EConnectivityProperties.PROP_ID_DATABASE.getPropId());
        database.setDatabaseName(property);
        bds.add(database);
        container.setDatabases(bds);
        try {
            List<MetadataTable> metadataTables = MetadataUtils.getMetadataTables(database, connection);
            database.setTables(metadataTables);
        } catch (Exception ex) {
            ExceptionOperation.operate(ex);
        }
        return container;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#importDemoProject()
     */
    public void importDemoProject(Shell shell, IProgressMonitor monitor) {
        String prjName = IComponentsFactory.DENGUES_DEMOS_NAME;
        Bundle bundle = DesignerCorePlugin.getDefault().getBundle();
        URL componentURL = FileLocator.find(bundle, new Path(IComponentsFactory.DENGUES_DEMOS_NAME), null);
        URL url;
        try {
            url = FileLocator.toFileURL(componentURL);
            String sourcePath = url.getPath();
            ImportDemoProjectProvider.importProjectAs(shell, prjName, sourcePath, monitor);
        } catch (IOException e) {
            ExceptionOperation.operate(e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getGeneratingNodes(org.dengues.core.process.ICompProcess)
     */
    public List<ICompNode> getGeneratingNodes(ICompProcess compProcess) {
        CompDataProcess dataProcess = new CompDataProcess(compProcess);
        dataProcess.buildDataNodes();
        return dataProcess.getDataNodes();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getLibPath(org.dengues.core.module.EClasspathVariables)
     */
    public String getLibPath(EClasspathVariables variables) {
        return SqldbUtil.getHsqldbJarPath(variables);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getLibURL(org.dengues.core.module.EClasspathVariables)
     */
    public URL getLibURL(EClasspathVariables variables) {
        return SqldbUtil.getHsqldbJarURL(variables);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getScriptSynchronizer()
     */
    public IModuleScriptSynchronizer getScriptSynchronizer() {
        return new ModuleScriptSynchronizer();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getXMLPreviewRows()
     */
    public List<String[]> getXMLPreviewRows(String xmlFile, String loopRoot, List<Map<String, Object>> mapping, String encoding) {
        ShadowFileInputNode inputNode = new ShadowFileInputNode(EGlobalComponentNames.ZXMLFILEINPUT, mapping.size());
        inputNode.setNodeParameterValue(FILENAME, FileUtils.getPortablePath(xmlFile));
        inputNode.setNodeParameterValue(LOOP_QUERY, loopRoot);
        inputNode.setNodeParameterValue(MAPPING, mapping);
        inputNode.setNodeParameterValue(ENCODING, encoding);

        String path = launchShadowProcess(inputNode, mapping);
        return FileUtils.getCSVArray(new File(path));
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#refreshBlocks()
     */
    public void refreshBlocks() {
        List<Object> allProcess = WarehouseResourceFactory.getAllObjectsFromEMF(ENodeCategoryName.PROCESS);
        Set<String> usedIds = new HashSet<String>();
        for (Object object : allProcess) {
            ProcessType type = (ProcessType) object;
            EList<BlockType> block = type.getBlock();
            for (BlockType blockType : block) {
                usedIds.add(blockType.getUuid());
            }
        }
        List<Object> allBlock = WarehouseResourceFactory.getAllObjectsFromEMF(ENodeCategoryName.BLOCKS);
        for (Object object2 : allBlock) {
            ProcessType processType = ((ProcessType) object2);
            if (!usedIds.contains(processType.getUuid())) {
                WarehouseResourceFactory.deleteObjectDataPhysical(object2);
                logger.info("Delete Block: Name=" + processType.getName() + "  UUID=" + processType.getUuid());
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getCVSPreviewRows(java.lang.String, java.lang.String,
     * java.lang.String, java.lang.String)
     */
    public List<String[]> getCVSPreviewRows(String cvsFile, String seqerator, String header, String enCoding) {
        CsvReader cr = null;
        int columnCount = 0;
        try {
            cr = new CsvReader(new BufferedReader(new InputStreamReader(new FileInputStream(cvsFile), "UTF-8")), seqerator
                    .charAt(0));
            cr.setSkipEmptyRecords(true);
            cr.setEscapeMode(CsvReader.ESCAPE_MODE_DOUBLED);

            for (int i = 0; cr.readRecord(); i++) {
                int temp = cr.getColumnCount();
                if (temp > columnCount) {
                    columnCount = temp;
                }
            }
        } catch (Exception e) {
            // e.printStackTrace();
        } finally {
            if (cr != null) {
                cr.close();
            }
        }

        ShadowFileInputNode inputNode = new ShadowFileInputNode(EGlobalComponentNames.ZCSVFILEINPUT, columnCount);
        inputNode.setNodeParameterValue(FILENAME, FileUtils.getPortablePath(cvsFile));
        inputNode.setNodeParameterValue(CSV_SEPARATOR, seqerator);
        inputNode.setNodeParameterValue(CSV_HEADER, header);
        inputNode.setNodeParameterValue(ENCODING, enCoding);

        String path = launchShadowProcess(inputNode, null);
        return FileUtils.getCSVArray(new File(path));
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "launchShadowProcess".
     * 
     * @param inputNode
     * @return
     */
    private String launchShadowProcess(ShadowFileInputNode inputNode, List<Map<String, Object>> mapping) {
        String path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toPortableString();
        path += DenguesProjectFactory.getTempFolder().getFullPath();
        path += "/" + "shadow.csv";
        IFile file = DenguesProjectFactory.getTempFolder().getFile("shadow.csv");
        if (file.exists()) {
            try {
                file.delete(true, null);
            } catch (CoreException e) {
                e.printStackTrace();
            }
        }

        CSVFileOutputNode outputNode = new CSVFileOutputNode(FileUtils.getPortablePath(path));
        if (mapping != null) {
            List<MetadataTable> tables = new ArrayList<MetadataTable>();
            MetadataTable table = new MetadataTable();
            table.setTableName(inputNode.getUniqueName());
            for (Map<String, Object> map : mapping) {
                Object object = map.get(IXMLComponent.COLUMN_SCHEMA);
                MetadataColumn column = new MetadataColumn();
                column.setType("VARCHAR");
                column.setColumnName(object.toString());
                column.setDefault("");
                table.getColumns().add(column);
            }
            tables.add(table);
            inputNode.setMetadataList(tables);
            tables = new ArrayList<MetadataTable>();
            tables.add(table.clone());
            outputNode.setMetadataList(tables);
        }
        ShadowCompProcess shadowCompProcess = new ShadowCompProcess(inputNode, outputNode);
        final JavaETLProcessor javaETLProcessor = new JavaETLProcessor(shadowCompProcess, new Path("shadow")); //$NON-NLS-1$
        try {
            javaETLProcessor.launch(false);
        } catch (CoreException e) {
            ExceptionOperation.operate(e);
        }
        return path;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.IDesignerCoreService#getExcelPreviewRows(java.util.HashMap)
     */
    public List<String[]> getExcelPreviewRows(HashMap<String, Object> properties, List<Map<String, Object>> mapping) {
        ShadowFileInputNode inputNode = new ShadowFileInputNode(EGlobalComponentNames.ZEXCELINPUT, mapping.size());
        for (String ket : properties.keySet()) {
            inputNode.setNodeParameterValue(ket, properties.get(ket));
        }
        String path = launchShadowProcess(inputNode, mapping);
        return FileUtils.getCSVArray(new File(path));
    }

}
